package com.boarsoft.rpc.generalize;

import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class RpcGenCollection extends RpcGenType implements Iterable<Object> {
	private static final long serialVersionUID = 6161040310515841508L;

	protected Collection<Object> collection;

	/** 为兼容GenericCollection增加：元素类型 */
	protected String elClzName = "java.lang.Object";

	public static RpcGenCollection newInstance(Collection<Object> collection) {
		return new RpcGenCollection(collection);
	}

	protected RpcGenCollection() {
	}

	protected RpcGenCollection(Collection<Object> collection) {
		this.setCollection(collection);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Collection<Object> specialize() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		Collection<Object> tc = (Set<Object>) this.create(clazzName);
		for (Object a : collection) {
			if (a != null && a instanceof RpcGenType) {
				Object o = ((RpcGenType) a).specialize();
				tc.add(o);
			} else {
				tc.add(a);
			}
		}
		return tc;
	}

	public void setCollection(Collection<Object> collection) {
		if (elClzName.equals("javan.lang.Object")) {
			if (collection != null && !collection.isEmpty()) {
				for (Object e : collection) {
					if (e != null) {
						this.elClzName = e.getClass().getName();
						break;
					}
				}
			}
		}
		this.collection = collection;
	}

	public String getElClzName() {
		return elClzName;
	}

	public void setElClzName(String elClzName) {
		this.elClzName = elClzName;
	}

	public Collection<Object> getCollection() {
		return collection;
	}

	@Override
	public boolean equals(Object o) {
		return collection.equals(o);
	}

	@Override
	public int hashCode() {
		return collection.hashCode();
	}

	// @Override
	public int size() {
		return collection.size();
	}

	// @Override
	public boolean isEmpty() {
		return collection.isEmpty();
	}

	// @Override
	public boolean contains(Object o) {
		return collection.contains(o);
	}

	@Override
	public Iterator<Object> iterator() {
		return collection.iterator();
	}

	// @Override
	public Object[] toArray() {
		return collection.toArray();
	}

	// @Override
	public boolean add(Object e) {
		if (elClzName.equals("javan.lang.Object")) {
			if (e != null) {
				this.elClzName = e.getClass().getName();
			}
		}
		return collection.add(e);
	}

	// @Override
	public boolean remove(Object o) {
		return collection.remove(o);
	}

	// @Override
	public boolean containsAll(Collection<Object> c) {
		return collection.containsAll(c);
	}

	// @Override
	public boolean addAll(Collection<Object> c) {
		if (c.isEmpty()) {
			return true;
		}
		if (elClzName.equals("javan.lang.Object")) {
			for (Object e : c) {
				if (e != null) {
					this.elClzName = e.getClass().getName();
					break;
				}
			}
		}
		return collection.addAll(c);
	}

	// @Override
	public boolean removeAll(Collection<?> c) {
		return collection.removeAll(c);
	}

	// @Override
	public boolean retainAll(Collection<?> c) {
		return collection.retainAll(c);
	}

	// @Override
	public void clear() {
		collection.clear();
	}

	// @Override
	public <T> T[] toArray(T[] a) {
		return collection.toArray(a);
	}

	/**
	 * Creates a {@link Spliterator} over the elements in this collection.
	 *
	 * Implementations should document characteristic values reported by the
	 * spliterator. Such characteristic values are not required to be reported
	 * if the spliterator reports {@link Spliterator#SIZED} and this collection
	 * contains no elements.
	 *
	 * <p>
	 * The default implementation should be overridden by subclasses that can
	 * return a more efficient spliterator. In order to preserve expected
	 * laziness behavior for the {@link #stream()} and
	 * {@link #parallelStream()}} methods, spliterators should either have the
	 * characteristic of {@code IMMUTABLE} or {@code CONCURRENT}, or be
	 * <em><a href="Spliterator.html#binding">late-binding</a></em>. If none of
	 * these is practical, the overriding class should describe the
	 * spliterator's documented policy of binding and structural interference,
	 * and should override the {@link #stream()} and {@link #parallelStream()}
	 * methods to create streams using a {@code Supplier} of the spliterator, as
	 * in:
	 * 
	 * <pre>
	 * {@code
	 *     Stream<E> s = StreamSupport.stream(() -> spliterator(), spliteratorCharacteristics)
	 * }
	 * </pre>
	 * <p>
	 * These requirements ensure that streams produced by the {@link #stream()}
	 * and {@link #parallelStream()} methods will reflect the contents of the
	 * collection as of initiation of the terminal stream operation.
	 *
	 * @implSpec The default implementation creates a
	 *           <em><a href="Spliterator.html#binding">late-binding</a></em>
	 *           spliterator from the collections's {@code Iterator}. The
	 *           spliterator inherits the <em>fail-fast</em> properties of the
	 *           collection's iterator.
	 *           <p>
	 *           The created {@code Spliterator} reports
	 *           {@link Spliterator#SIZED}.
	 *
	 * @implNote The created {@code Spliterator} additionally reports
	 *           {@link Spliterator#SUBSIZED}.
	 *
	 *           <p>
	 *           If a spliterator covers no elements then the reporting of
	 *           additional characteristic values, beyond that of {@code SIZED}
	 *           and {@code SUBSIZED}, does not aid clients to control,
	 *           specialize or simplify computation. However, this does enable
	 *           shared use of an immutable and empty spliterator instance (see
	 *           {@link Spliterators#emptySpliterator()}) for empty collections,
	 *           and enables clients to determine if such a spliterator covers
	 *           no elements.
	 *
	 * @return a {@code Spliterator} over the elements in this collection
	 * @since 1.8
	 */
	public Spliterator<Object> spliterator() {
		return Spliterators.spliterator(this.getCollection(), 0);
	}

	/**
	 * Returns a sequential {@code Stream} with this collection as its source.
	 *
	 * <p>
	 * This method should be overridden when the {@link #spliterator()} method
	 * cannot return a spliterator that is {@code IMMUTABLE},
	 * {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()}
	 * for details.)
	 *
	 * @implSpec The default implementation creates a sequential {@code Stream}
	 *           from the collection's {@code Spliterator}.
	 *
	 * @return a sequential {@code Stream} over the elements in this collection
	 * @since 1.8
	 */
	public Stream<Object> stream() {
		return StreamSupport.stream(spliterator(), false);
	}

	/**
	 * Returns a possibly parallel {@code Stream} with this collection as its
	 * source. It is allowable for this method to return a sequential stream.
	 *
	 * <p>
	 * This method should be overridden when the {@link #spliterator()} method
	 * cannot return a spliterator that is {@code IMMUTABLE},
	 * {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()}
	 * for details.)
	 *
	 * @implSpec The default implementation creates a parallel {@code Stream}
	 *           from the collection's {@code Spliterator}.
	 *
	 * @return a possibly parallel {@code Stream} over the elements in this
	 *         collection
	 * @since 1.8
	 */
	public Stream<Object> parallelStream() {
		return StreamSupport.stream(spliterator(), true);
	}
}
